/* test_mutex_func_uthread_mutex_destroy():
	A test for uthread_mutex_destroy(). Checks the regular case + the error
	cases.

	Does:
	* Creates an additional thread: thread 1.

	* Checks for MUTEX_INVALID error.

	* Thread 1 trys to destroy an uninitilized mutex, and fails.

	And then:

	1) Thread 1 initializes a new mutex (A), locks it. Fails to destroy
	  it, then unlocks it, and then successfully destroys it. Then fails to
	  destroy it again.

	2) Thread 0 initilize and locks mutex B twise. thread 1 trys to destroy it
	  and fails. Then thread 0 unlock it twise, and thread 1 successfuly destroy
	  it. Then thread 0 fails to destroy it again.
*/


#include <stdio.h>
#include <stdbool.h>

#include "test_general.h"
#include "user_threads.h"
#include "user_mutex.h"


bool test_mutex_func_uthread_mutex_destroy();


#define THREADS_NUM 2



uthread_mutex_t mutex_b;


static int thread1_start_routine(void * param) {
	if ( (*((int*)param)) != 53) {
		return -1;
	}

	TEST_ASSERT (uthread_self() == 1);

	int tmp;
	tmp = uthread_mutex_destroy(NULL);
	TEST_ASSERT (MUTEX_INVALID == tmp);

	char tmp_char;
	tmp = uthread_mutex_destroy((uthread_mutex_t)&tmp_char);
	TEST_ASSERT (MUTEX_UNINITIALIZED == tmp);


	// Check 1:
	uthread_mutex_t mutex_a;
	tmp = uthread_mutex_init(&mutex_a);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);

	tmp = uthread_mutex_lock(mutex_a);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);

	tmp = uthread_mutex_destroy(mutex_a);
	TEST_ASSERT (MUTEX_LOCKED == tmp);

	tmp = uthread_mutex_unlock(mutex_a);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);

	tmp = uthread_mutex_destroy(mutex_a);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);

	tmp = uthread_mutex_destroy(mutex_a);
	TEST_ASSERT (MUTEX_UNINITIALIZED == tmp);


	// Check 2:
	tmp = uthread_mutex_destroy(mutex_b);
	TEST_ASSERT (MUTEX_LOCKED == tmp);

	some_long_operation(0.2);
	// Thread 0 unlocked it.

	tmp = uthread_mutex_destroy(mutex_b);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);

	thread_ok[1] = true;
	return 0;
}

bool test_mutex_func_uthread_mutex_destroy()
{
	init_individual_test();

	int tmp;

	if (initilize_uthread_in_each_test) {
		tmp = uthread_init();
		TEST_ASSERT(tmp == UTHREAD_SUCCESS);
	} 

	printf("after init \n");

	tmp = uthread_mutex_init(&mutex_b);
	TEST_ASSERT(tmp == MUTEX_SUCCESS);
	printf("after init b \n");
	tmp = uthread_mutex_lock(mutex_b);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);
	printf("after lock b\n");
	/*tmp = uthread_mutex_lock(mutex_b);
	TEST_ASSERT (MUTEX_SUCCESS == tmp); // we cant lock the same mutex twice ! */
	tmp = uthread_mutex_try_lock(mutex_b);
	printf( " tmp  = %d , fail : %d , success : %d ", tmp, MUTEX_FAILURE, MUTEX_SUCCESS);
	TEST_ASSERT (MUTEX_FAILURE == tmp);
	printf("after try lock b again \n");

	thread_id threads[THREADS_NUM];

	threads[0] = uthread_self();
	TEST_ASSERT(threads[0] == 0);
	printf("after self \n");

	int FiftyThree = 53;

	threads[1] = uthread_create(thread1_start_routine, &FiftyThree);
	TEST_ASSERT(threads[1] == 1);

	printf("after create \n");

	some_long_operation(0.1);


	printf("after wait \n");

	tmp = uthread_mutex_unlock(mutex_b);
	TEST_ASSERT (MUTEX_SUCCESS == tmp);
	printf("after unlock b \n");
	tmp = uthread_mutex_unlock(mutex_b);
	TEST_ASSERT (MUTEX_UNLOCKED == tmp);

	printf("after unlock b2 \n");

	some_long_operation(0.3);
	// Thread 1 destroyed mutex_b.


	tmp = uthread_mutex_destroy(mutex_b);
	TEST_ASSERT (MUTEX_UNINITIALIZED == tmp);

	tmp = uthread_join(threads[1],NULL);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);


	thread_ok[0] = true;
	TEST_ASSERT(are_all_threads_ok(THREADS_NUM));

	return true;
}

